To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.
In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.
Save/Validate Hooks
The save
method will trigger validate
hooks.
This is because Mongoose calls the pre('save')
hook that calls validate
.
The pre('validate')
and post('validate')
hooks are called before any pre('save')
hooks.
For example, we can write:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({ name: String });
schema.pre('validate', () => {
console.log('1');
});
schema.post('validate', () => {
console.log('2');
});
schema.pre('save', () => {
console.log('3');
});
schema.post('save', () => {
console.log('4');
});
const User = connection.model('User', schema);
new User({ name: 'test' }).save();
}
run();
to add the schema hooks.
Then they’ll be called one by one in the same order that they’re listed.
Query Middleware
Pre and post save
hooks aren’t run when update methods are run.
For example, if we have:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({ name: String });
schema.pre('updateOne', { document: true, query: false }, function() {
console.log('Updating');
});
const User = connection.model('User', schema);
const doc = new User();
await doc.updateOne({ $set: { name: 'test' } });
await User.updateOne({}, { $set: { name: 'test' } });
}
run();
Then when we have query
set to false
or didn’t add the query
property, then the updateOne
pre hook won’t run when we run updateOne
.
Aggregation Hooks
We can add aggregation hooks.
For example, we can write:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({
name: {
type: String,
unique: true
}
});
schema.pre('aggregate', function() {
this.pipeline().unshift({ $match: { isDeleted: { $ne: true } } });
});
const User = connection.model('User', schema);
}
run();
We listen to the aggregate
event.
Error Hooks
We can get errors from hooks.
For example, we can write:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({
name: {
type: String,
unique: true
}
});
schema.post('update', function (error, res, next) {
if (error.name === 'MongoError' && error.code === 11000) {
next(new Error('There was a duplicate key error'));
} else {
next();
}
});
const User = connection.model('User', schema);
}
run();
We listen to the update
event and get the error from the error
parameter.
We can get the name
and code
to get information about the error.
Synchronous Hooks
Some hooks are always synchronous.
init
hooks are always synchronous because the init
function is synchronous.
For example, if we have:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({
name: String
});
schema.pre('init', obj => {
console.log(obj);
});
const User = connection.model('User', schema);
}
run();
We added the pre init
hook with a callback.
Conclusion
We can add middleware for various events that are emitted when we manipulate data with Mongoose.